# Copyright (C) 2017 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

package QtQA::Proc::Reliable::TESTDATA;
use strict;
use warnings;

=head1 NAME

QtQA::Proc::Reliable::TESTDATA - testdata for system tests of QtQA::Proc::Reliable

=head1 DESCRIPTION

This module is not intended for use by the end-user of QtQA::Proc::Reliable.
It is merely a holding area for a suite of testdata to be used in a few autotests
relating to QtQA::Proc::Reliable.

=cut

use Tie::IxHash;
use Readonly;

use base 'Exporter';
our @EXPORT_OK = qw( %TESTDATA );

# Helper regex portions for below testdata
Readonly my %RE => (

    # Matches the text which states we're about to retry a command (not including the reason)
    retry => qr{ \QQtQA::TestScript: It will be retried because\E }xms,

    # Matches the reason why a command was restarted.
    # These are set by the specific QtQA::Proc::Reliable strategies and are cumbersome
    # to test, so we simply allow for any properly formatted text.
    retry_reason => qr{
        (?:
            [^\n]                   # reason fits on a single line ...
            |                       #
            \nQtQA::TestScript:     # ... or it takes multiple lines, all of them prefixed
        )*
    }xms,

    # Matches the prefix on the line where the attempted command is printed
    command => qr{
        \QQtQA::TestScript:     \E\$command
    }xms,

    # Matches the prefix for "the (n'th) attempt at running this command ..."
    # Usage example: $RE{ attempt }{ second }
    attempt => {
        map {
            $_ => qr{ \QQtQA::TestScript: The $_ attempt at running this command:\E }xms
        }
        qw(
            first
            second
            third
            fourth
            fifth
            sixth
        ) # add any more as needed
    },

    # Matches the text generated by failing with an exit code
    # Usage example: $RE{ failed_with_exitcode }{ 12 }
    failed_with_exitcode => {
        map {
            $_ => qr{ \QQtQA::TestScript: ... failed with exit code $_.\E }xms
        }
        (1..255)
    },

);


# This is tied to retain order, as we want the tests to be run in order from least
# to most complex.
#
# Note, can't be Readonly without causing Params::Validate to segfault :(
tie our %TESTDATA => 'Tie::IxHash',

    # simple passing command to ensure correct operation in the normal case
    'git control' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 0, stdout => "Hi\n" },
            ],
        },
        expected_raw_stdout => "Hi\n",
    },

    # recoverable git problem
    'git remote end hung up' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 12, stderr => "fatal: The remote end hung up unexpectedly\n" },
                { exitcode => 12, stderr => "fatal: The remote end hung up unexpectedly\n" },
                { exitcode => 0 },
            ],
        },
        expected_retries    => 2,
        expected_exe_stderr => qr"

            \A

            \Qfatal: The remote end hung up unexpectedly\E                                      \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['git','clone','git://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 12 }                                                   \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qfatal: The remote end hung up unexpectedly\E                                      \n
            $RE{ attempt }{ second }                                                            \n
            $RE{ command } \Q = ['git','clone','git://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 12 }                                                   \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \z

        "xms,
        expected_raw_stderr => "fatal: The remote end hung up unexpectedly\n" x 2,
    },

    # error message but 0 exit code; make sure we don't retry
    'git remote end hung up with 0 exit code' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 0, stderr => "fatal: The remote end hung up unexpectedly\n" },
            ],
        },
        expected_retries    => 0,
        expected_raw_stderr => "fatal: The remote end hung up unexpectedly\n",
        expected_exe_stderr => "fatal: The remote end hung up unexpectedly\n",
    },

    # unrecoverable git problem with no retries
    'git unrecoverable simple' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 12, stdout => "Cloning...\n", stderr => "fatal: Foo bar the baz\n" },
            ],
        },
        expected_raw_stderr => "fatal: Foo bar the baz\n",
        expected_raw_stdout => "Cloning...\n",
        expected_status     => (12 << 8),
    },

    # unrecoverable git problem with some retries
    'git unrecoverable with retry' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 1,  stderr => "fatal: The remote end hung up unexpectedly\n" },
                { exitcode => 2,  stderr => "fatal: The remote end hung up unexpectedly\n" },
                { exitcode => 58, stderr => "fatal: Foo bar the baz\n" },
            ],
        },
        expected_retries    => 2,
        expected_exe_stderr => qr"

            \A

            \Qfatal: The remote end hung up unexpectedly\E                                      \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['git','clone','git://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 1 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qfatal: The remote end hung up unexpectedly\E                                      \n
            $RE{ attempt }{ second }                                                            \n
            $RE{ command } \Q = ['git','clone','git://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 2 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qfatal: Foo bar the baz\E                                                          \n

            \z

        "xms,
        expected_raw_stderr => ("fatal: The remote end hung up unexpectedly\n" x 2)
                               ."fatal: Foo bar the baz\n",
        expected_status     => (58 << 8),
    },

    # git DNS failure - git:// protocol
    'git DNS failure git://' => {
        command             => [ 'git', 'clone', 'git://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 128, stderr => "fatal: Unable to look up example.com (port 9418) (Name or service not known)\n" },
                { exitcode => 0 },
            ],
        },
        expected_retries    => 1,
        expected_exe_stderr => qr"

            \A

            \Qfatal: Unable to look up example.com (port 9418) (Name or service not known)\E    \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['git','clone','git://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 128 }                                                  \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \z

        "xms,
        expected_raw_stderr => "fatal: Unable to look up example.com (port 9418) (Name or service not known)\n",
    },

    # git DNS failure - ssh:// protocol
    # This test basically confirms that the Git strategy is subclassing the ssh strategy as
    # expected.  Other ssh:// protocol git issues aren't tested further.
    'git DNS failure ssh://' => {
        command             => [ 'git', 'clone', 'ssh://example.com/repo.git' ],
        mock_command        => {
            sequence        => [
                { exitcode => 128, stderr => "ssh: Could not resolve hostname example.com: Name or service not known\n" },
                { exitcode => 0 },
            ],
        },
        expected_retries    => 1,
        expected_exe_stderr => qr"

            \A

            \Qssh: Could not resolve hostname example.com: Name or service not known\E          \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['git','clone','ssh://example.com/repo.git'];\E                 \n
            $RE{ failed_with_exitcode }{ 128 }                                                  \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \z

        "xms,
        expected_raw_stderr => "ssh: Could not resolve hostname example.com: Name or service not known\n",
    },


    # various scp problems
    'scp network issues ultimately fatal' => {
        command             => [ 'scp', 'example.com:~/file1', '.' ],
        mock_command        => {
            sequence        => [
                { exitcode => 1, stderr => "ssh: Could not resolve hostname example.com: Name or service not known\n" },
                { exitcode => 2, stderr => "ssh: connect to host example.com: No route to host\n" },
                { exitcode => 3, stderr => "ssh: Connect to host example.com: Network is unreachable\n" },
                { exitcode => 4, stderr => "ssh: connect to host example.com port 1234: Connection timed out\n" },
                { exitcode => 5, stderr => "ssh: connect to host example.com port 22: Connection refused\n" },

                # check that stdout does _not_ cause the command to be retried
                { exitcode => 6, stdout => "ssh: connect to host example.com port 23: Connection refused\n" },
            ],
        },
        expected_retries    => 5,
        expected_exe_stderr => qr"

            \A

            \Qssh: Could not resolve hostname example.com: Name or service not known\E          \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['scp','example.com:~/file1','.'];\E                            \n
            $RE{ failed_with_exitcode }{ 1 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qssh: connect to host example.com: No route to host\E                              \n
            $RE{ attempt }{ second }                                                            \n
            $RE{ command } \Q = ['scp','example.com:~/file1','.'];\E                            \n
            $RE{ failed_with_exitcode }{ 2 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qssh: Connect to host example.com: Network is unreachable\E                        \n
            $RE{ attempt }{ third }                                                             \n
            $RE{ command } \Q = ['scp','example.com:~/file1','.'];\E                            \n
            $RE{ failed_with_exitcode }{ 3 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qssh: connect to host example.com port 1234: Connection timed out\E                \n
            $RE{ attempt }{ fourth }                                                            \n
            $RE{ command } \Q = ['scp','example.com:~/file1','.'];\E                            \n
            $RE{ failed_with_exitcode }{ 4 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qssh: connect to host example.com port 22: Connection refused\E                    \n
            $RE{ attempt }{ fifth }                                                             \n
            $RE{ command } \Q = ['scp','example.com:~/file1','.'];\E                            \n
            $RE{ failed_with_exitcode }{ 5 }                                                    \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \z

        "xms,
        expected_raw_stderr =>
            "ssh: Could not resolve hostname example.com: Name or service not known\n"
           ."ssh: connect to host example.com: No route to host\n"
           ."ssh: Connect to host example.com: Network is unreachable\n"
           ."ssh: connect to host example.com port 1234: Connection timed out\n"
           ."ssh: connect to host example.com port 22: Connection refused\n"
        ,
        expected_raw_stdout => "ssh: connect to host example.com port 23: Connection refused\n",
        expected_status     => (6 << 8),
    },


    # simple check with explicitly loading a strategy
    'non-auto ssh failure' => {
        command             => [ 'frobnitz' ],
        reliable            => [ 'ssh' ],
        mock_command        => {
            sequence        => [
                # check that the requested `reliable' loaded ssh strategy ...
                { exitcode => 128, stderr => "ssh: Could not resolve hostname example.com: Name or service not known\n" },
                # ...and did _not_ load `git' strategy
                { exitcode => 128, stderr => "fatal: Unable to look up example.com (port 9418) (Name or service not known)\n" },
            ],
        },
        expected_retries    => 1,
        expected_exe_stderr => qr"

            \A

            \Qssh: Could not resolve hostname example.com: Name or service not known\E          \n
            $RE{ attempt }{ first }                                                             \n
            $RE{ command } \Q = ['frobnitz'];\E                                                 \n
            $RE{ failed_with_exitcode }{ 128 }                                                  \n
            $RE{ retry } $RE{ retry_reason }                                                    \n

            \Qfatal: Unable to look up example.com (port 9418) (Name or service not known)\E    \n

            \z

        "xms,
        expected_raw_stderr =>
            "ssh: Could not resolve hostname example.com: Name or service not known\n"
           ."fatal: Unable to look up example.com (port 9418) (Name or service not known)\n"
        ,
        expected_status     => (128 << 8),
    },

;

1;
